﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Model
{
    [Serializable()]
    public class Rek : ISerializable
    {
        static bool PointInPolygon(Point p, Point[] poly)
        {
            Point p1, p2;
            bool inside = false;
            if (poly.Length < 3)
            {
                return inside;
            }

            Point oldPoint = new Point(poly[poly.Length - 1].X, poly[poly.Length - 1].Y);

            for (int i = 0; i < poly.Length; i++)
            {
                Point newPoint = new Point(poly[i].X, poly[i].Y);

                if (newPoint.X > oldPoint.X)
                {
                    p1 = oldPoint;
                    p2 = newPoint;
                }
                else
                {
                    p1 = newPoint;
                    p2 = oldPoint;
                }

                if ((newPoint.X < p.X) == (p.X <= oldPoint.X)
                && ((long)p.Y - (long)p1.Y) * (long)(p2.X - p1.X)
                 < ((long)p2.Y - (long)p1.Y) * (long)(p.X - p1.X))
                {
                    inside = !inside;
                }

                oldPoint = newPoint;
            }

            return inside;
        }

        private int nummer;
        private int zijdeteller;
        private Point[] punten;
        private double[] hoeken1;
        private double[] hoeken2;
        private bool perfecteRechthoek;
        private List<Zijde> zijden;

      
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Nummer", this.nummer);
            info.AddValue("Zijdeteller", this.zijdeteller);
            info.AddValue("Punten", this.punten);
            info.AddValue("Hoeken1", this.hoeken1);
            info.AddValue("Hoeken2", this.hoeken2);
            info.AddValue("PerfecteRechthoek", this.perfecteRechthoek);
            info.AddValue("Zijden", this.Zijden);
        }

        /// <summary>
        /// Het nummer van het rek
        /// </summary>
        public int Nummer
        {
            get { return nummer; }
            set { nummer = value; }
        }

        /// <summary>
        /// De zijden van het rek
        /// </summary>
        public List<Zijde> Zijden
        {
            get { return zijden; }
        }

        #region Constructors
        public Rek()
        {
        }

        public Rek(SerializationInfo info, StreamingContext ctxt)
        {
            this.nummer = (int)info.GetValue("Nummer", typeof(int));
            this.zijdeteller = (int)info.GetValue("Zijdeteller", typeof(int));
            this.punten = (Point[])info.GetValue("Punten", typeof(Point[]));
            this.hoeken1 = (double[])info.GetValue("Hoeken1", typeof(double[]));
            this.hoeken2 = (double[])info.GetValue("Hoeken2", typeof(double[]));
            this.perfecteRechthoek = (bool)info.GetValue("PerfecteRechthoek", typeof(bool));
            this.zijden = (List<Zijde>)info.GetValue("Zijden", typeof(List<Zijde>));
        }


        public Rek(Point punt1, Point punt2, Point punt3, Point punt4, int nummer)
        {
            zijdeteller = 0;
            this.nummer = nummer;
            punten = new Point[4];
            punten[0] = punt1;
            punten[1] = punt2;
            punten[2] = punt3;
            punten[3] = punt4;
            perfecteRechthoek = checker(punten);
            if (!perfecteRechthoek) { Orden(); }
            BerekenCoefficienten();
            this.zijden = new List<Zijde>();
        }

        public Rek(Point[] punten, int nummer)
        {
            zijdeteller = 0;
            this.nummer = nummer;
            this.punten = punten;
            perfecteRechthoek = checker(punten);
            if (!perfecteRechthoek) { Orden(); }
            BerekenCoefficienten();
            this.zijden = new List<Zijde>();
        }

        public Rek(List<Point> lijst, int nummer)
        {
            zijdeteller = 0;
            this.nummer = nummer;
            punten = new Point[4];
            punten[0] = lijst[0];
            punten[1] = lijst[1];
            punten[2] = lijst[2];
            punten[3] = lijst[3];
            perfecteRechthoek = checker(punten);
            if (!perfecteRechthoek) { Orden(); }
            BerekenCoefficienten();
            this.zijden = new List<Zijde>();
        }
      
        #endregion

        /// <summary>
        /// Zit het gegeven punt in het rek
        /// </summary>
        /// <param name="plaats">De plaats</param>
        /// <returns>True wanneer het punt in het rek zit, false wanneer niet</returns>
        public bool ZitInRek(Point plaats)
        {
            return PointInPolygon(plaats, punten);
            //return binnenRek(plaats); ikke niet zeker weet of werkt
        }

        /// <summary>
        /// De punten van het rek
        /// </summary>
        public Point[] Punten
        {
            get { return punten; }
            set
            { punten = value; }
        }


        /// <summary>
        /// Berekent de coefficienten
        /// </summary>
        private void BerekenCoefficienten()
        {
            if (!perfecteRechthoek)
            {
                hoeken1 = new double[2];
                hoeken2 = new double[2];
                //het y verschil
                double overstaande = punten[0].Y - punten[1].Y;
                //het x verschil
                double aanliggende = punten[1].X - punten[0].X;
                hoeken1[0] = Math.Abs(Math.Atan(overstaande / aanliggende));

                overstaande = punten[3].Y - punten[0].Y;
                aanliggende = punten[3].X - punten[0].X;
                hoeken1[1] = Math.Abs(Math.Atan(overstaande / aanliggende));

                overstaande = punten[2].Y - punten[1].Y;
                aanliggende = punten[2].X - punten[1].X;
                hoeken2[0] = Math.Abs(Math.Atan(overstaande / aanliggende));

                overstaande = punten[3].Y - punten[2].Y;
                aanliggende = punten[2].X - punten[3].X;
                hoeken2[1] = Math.Abs(Math.Atan(overstaande / aanliggende));
            }

        }

        /// <summary>
        /// Is het binnen het rek
        /// </summary>
        /// <param name="plaats">De plaats</param>
        /// <returns>True of false (binnen of buiten rek)</returns>
        private bool binnenRek(Point plaats)
        {
            if (perfecteRechthoek)
            {
                return EvenRek(plaats);
            }
            else
            {
                return OnEvenRek(plaats);
            }
        }

        private bool OnEvenRek(Point plaats)
        {
            double overstaande;
            double aanliggende;
            bool boven1;
            if (plaats.X == punten[0].X || plaats.Y == punten[0].Y) { }
            if (plaats.X == punten[0].X && plaats.Y == punten[0].Y || (plaats.X == punten[2].Y) && (plaats.Y == punten[2].Y)) { return true; }
            if (plaats.Y < punten[0].Y)
            {
                overstaande = punten[0].Y - plaats.Y;
                boven1 = true;
            }
            else
            {
                overstaande = plaats.Y - punten[0].Y;
                boven1 = false;
            }
            if (plaats.X > punten[0].X)
            {
                aanliggende = plaats.X - punten[0].X;
                double temp = Math.Atan(overstaande / aanliggende);
                if (boven1)
                {
                    if (temp > hoeken1[0]) { return false; }
                }
                else
                {
                    if (temp > hoeken1[1]) { return false; }
                }
            }
            else
            {
                return false;
            }

            if (plaats.X == punten[2].X || plaats.Y == punten[2].Y) { }
            if (plaats.Y < punten[2].Y)
            {
                overstaande = punten[2].Y - plaats.Y;
                boven1 = true;
            }
            else
            {
                overstaande = plaats.Y - punten[2].Y;
                boven1 = false;
            }
            if (plaats.X < punten[2].X)
            {
                aanliggende = punten[2].X - plaats.X;
                double temp = Math.Atan(overstaande / aanliggende);
                if (boven1)
                {
                    if (temp > hoeken1[0]) { return false; }
                }
                else
                {
                    if (temp > hoeken1[1]) { return false; }
                }
            }
            else
            {
                return false;
            }
            return true;
        }

        private bool EvenRek(Point plaats)
        {
            return false;
        }

        private void Orden()
        {
            Point[] temp = new Point[4];

            // De rechter wordt bepaald
            int t1 = Math.Min(punten[0].X, punten[1].X);
            int t2 = Math.Min(punten[2].X, punten[3].X);
            int tr = Math.Min(t2, t1);

            // De bovenste wordt bepaald
            t1 = Math.Min(punten[0].Y, punten[1].Y);
            t2 = Math.Min(punten[2].Y, punten[3].Y);
            int tb = Math.Min(t2, t1);

            // De linker wordt bepaald
            t1 = Math.Max(punten[0].X, punten[1].X);
            t2 = Math.Max(punten[2].X, punten[3].X);
            int tl = Math.Max(t2, t1);

            // De onderste wordt bepaald
            t1 = Math.Max(punten[0].Y, punten[1].Y);
            t2 = Math.Max(punten[2].Y, punten[3].Y);
            int to = Math.Max(t2, t1);

            //alle punten worden doorlopen
            for (int i = 0; i < 4; i++)
            {
                if (tr == punten[i].X) { temp[0] = punten[i]; }
                if (tb == punten[i].Y) { temp[1] = punten[i]; }
                if (tl == punten[i].X) { temp[2] = punten[i]; }
                if (to == punten[i].Y) { temp[3] = punten[i]; }
            }
        }

        private bool checker(Point[] hoeken)
        {
            return (hoeken[0].X == hoeken[1].X || hoeken[1].X == hoeken[2].X || hoeken[2].X == hoeken[3].X || hoeken[0].X == hoeken[2].X || hoeken[0].X == hoeken[3].X || hoeken[1].X == hoeken[3].X || hoeken[0].Y == hoeken[1].Y || hoeken[1].Y == hoeken[2].Y || hoeken[2].Y == hoeken[3].Y || hoeken[0].Y == hoeken[2].Y || hoeken[0].Y == hoeken[3].Y || hoeken[1].Y == hoeken[3].Y);
        }


    }
}
